#ifndef RAY_TRACE_PRO_RENDERER_H
#define RAY_TRACE_PRO_RENDERER_H

#include "Windows/GxThreads.h"
#include "Basic/GxLibBasic.h"
#include "VectorMath.h"
#include "RenderBuffer.h"
#include "CameraProjection.h"
#include "SobolSequenceGenerator.h"

using namespace GxLibMath;

namespace RayTracePro
{
	class Scene;
	class DifferentialGeometry;
	class RayDifferential;
	class Ray;
	class RayTraceSystem;
	class RenderThreadStorage;

	class Renderer : public GxLibBasic::Object
	{
		friend class RayTraceSystemImpl;
	private:
		RayTraceSystem * system;
		Scene * scene;
		int multiSample;
		float invMultiSample;
		RenderBuffer * screenBuffer;
		GxLibBasic::Array<RenderThreadStorage*, true> threadStorages;
		Renderer(RayTraceSystem * system);
	public:
		~Renderer();
		CameraProjection Camera;
		void SetScene(Scene * _scene);
		Scene * GetScene();
		void SetPerspective(int width, int height, float fovy);
		void RenderPixel(Vec4 & rs, DifferentialGeometry &dgeom, int x, int y);
		void RenderPixelMultiSampled(Vec4 & rs, DifferentialGeometry &dgeom, int x, int y, SobolGenerator & randGen);
		void RenderBlock(int x, int y, int w, int h);
		RenderBuffer * GetScreenBuffer();
		void SetScreenBuffer(RenderBuffer * buffer);
		void AllocateThreadStorage(int threads);
		void DebugDrawPhotons();
		void PrepareRendering();
		void SetMultiSample(int value)
		{
			multiSample = max(1, min(64, value));
			invMultiSample = 1.0f/multiSample;
		}
		int GetMultiSample()
		{
			return multiSample;
		}
		RenderThreadStorage * GetThreadStorage(int id)
		{
			return threadStorages[id];
		}
		RayTraceSystem * GetSystem()
		{
			return system;
		}
	};

	struct RenderTask
	{
		int x, y;
		int w, h;
	};

	class RenderThreadScheduler : public GxLibBasic::Object
	{
	private:
		GxLibBasic::Array<GxLibBasic::RefPtr<GxWinSystem::Threading::gxThread>> threads;
		GxLibBasic::Array<RenderTask, true> tasks;
		Renderer * renderer;
		GxWinSystem::Threading::gxLock lock;
		int taskId;
		bool cancelled;
		volatile long exited_threads;
		volatile long remainingTasks;
		int GetNextTask();
		void RenderThread(Object * threadIdObj);
	public:
		GxLibBasic::Event<const RenderTask&> BlockAssigned;
		GxLibBasic::Event<const RenderTask&> BlockCompleted;
		GxLibBasic::Event<bool> RenderCompleted;

		RenderThreadScheduler(Renderer * renderer, int blockWidth, int blockHeight);
		~RenderThreadScheduler();
		void Render(int threads);
		void Cancel();
	};
}

#endif